/*
 *   Copyright 2021 zzh
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 */

package com.aduib.boot.common.serialization.protobuf;

import com.aduib.boot.common.serialization.api.ObjectWriter;
import com.google.protobuf.BoolValue;
import com.google.protobuf.ByteString;
import com.google.protobuf.BytesValue;
import com.google.protobuf.DoubleValue;
import com.google.protobuf.FloatValue;
import com.google.protobuf.Int32Value;
import com.google.protobuf.Int64Value;
import com.google.protobuf.StringValue;

import java.io.IOException;
import java.io.OutputStream;

public class GenericProtobufObjectWriter implements ObjectWriter {

  private final OutputStream os;

  public GenericProtobufObjectWriter(OutputStream os) {
    this.os = os;
  }

  @Override
  public void writeBoolean(boolean v) throws IOException {

    writeObject(BoolValue.newBuilder().setValue(v).build());
  }

  @Override
  public void writeByte(byte v) throws IOException {
    writeObject(Int32Value.newBuilder().setValue((v)).build());
  }

  @Override
  public void writeShort(short v) throws IOException {
    writeObject(Int32Value.newBuilder().setValue(v).build());
  }

  @Override
  public void writeInt(int v) throws IOException {
    writeObject(Int32Value.newBuilder().setValue(v).build());
  }

  @Override
  public void writeLong(long v) throws IOException {
    writeObject(Int64Value.newBuilder().setValue(v).build());
  }

  @Override
  public void writeFloat(float v) throws IOException {
    writeObject(FloatValue.newBuilder().setValue(v).build());
  }

  @Override
  public void writeDouble(double v) throws IOException {
    writeObject(DoubleValue.newBuilder().setValue(v).build());
  }

  @Override
  public void writeUtfString(String v) throws IOException {
    writeObject(StringValue.newBuilder().setValue(v).build());
  }

  @Override
  public void writeBytes(byte[] b) throws IOException {
    writeObject(BytesValue.newBuilder().setValue(ByteString.copyFrom(b)).build());
  }

  @Override
  public void writeBytes(byte[] b, int off, int len) throws IOException {
    writeObject(BytesValue.newBuilder().setValue(ByteString.copyFrom(b, off, len)).build());
  }

  @SuppressWarnings("unchecked")
  @Override
  public void writeObject(Object obj) throws IOException {
    /**
     * Protobuf does not allow writing of non-protobuf generated messages, including null value.
     * Writing of null value from developers should be denied immediately by throwing exception.
     */
    if (obj == null) {
      throw new IllegalStateException(
          "This serialization only supports google protobuf objects, "
              + "please use com.google.protobuf.Empty instead if you want to transmit null values.");
      // obj = ProtobufUtils.convertNullToEmpty();
    }
    if (!ProtobufUtils.isSupported(obj.getClass())) {
      throw new IllegalArgumentException(
          "This serialization only supports google protobuf objects, current object class is: "
              + obj.getClass().getName());
    }

    ProtobufUtils.serialize(obj, os);
    os.flush();
  }

  @Override
  public void flushBuffer() throws IOException {
    os.flush();
  }
}
